package com.dysoft.flesh.ui;

import com.dysoft.bones.*;
import com.dysoft.bones.Event;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Sean Micklethwaite
 *         Nov 6, 2010 5:51:29 PM
 *
 * Container class for non-overlapping, linerarly organised widgets, positioned
 * relative to the top-left corner of this container.
 *
 * TODO: Accepts a key map, which allows a child in the container to be selected
 * using the keyboard. Other keyboard events are forwarded to the active child.
 */
public class UiContainer extends UiWidget implements UiWidget.Container {
	final List<UiWidget> children = new ArrayList<UiWidget>();
	UiWidget activeChild = null;
	final Set<UiWidget> dynamicSet = new LinkedHashSet<UiWidget>();

	public UiContainer(Property<Shape> shape) {
		super(shape);
		setState(new State());
	}

	public UiContainer(Property.Delegate<Shape> shape) {
		super(shape);
		setState(new State());
	}

	public UiWidget addWidget(UiWidget widget) {
		children.add(widget);
		widget.onAdd(this);

		if(widget instanceof Dynamic) {
			addDynamic(widget);
		}

		if(container != null) repaint();
		return widget;
	}

	public void removeWidget(UiWidget widget) {
		children.remove(widget);
		if(activeChild == widget) {
			activeChild = null;
		}
		removeDynamic(widget);
		if(container != null) repaint();
	}

	@Override
	protected void onAdd(Container container) {
		super.onAdd(container);
		if(!dynamicSet.isEmpty()) {
			container.addDynamic(this);
		}
	}

	@Override
	public void render(Context ctx) throws DataManager.DataObject.NotPresentException {
		super.render(ctx);
		Rectangle ctrBounds = getShape().getBounds();

		ctx.pushOffset(new Point(
			ctx.getOffset().x + (int)ctrBounds.getMinX(), ctx.getOffset().y + (int)ctrBounds.getMinY()
		));


		for(UiWidget w : children) {
			WidgetRenderer.renderWidget(ctx, w);
		}

		ctx.popOffset();
	}

	public void repaint() {
		// TODO: region
		if(container != null)
			container.repaint();
	}

	public UiWidget getChildAt(int x, int y, int [] wxy) {
		int a = 0, b = children.size();
		while(a < b) {
			int m = a + (b - a) / 2;
			UiWidget w = children.get(m);
			Shape shape = w.getShape();
			Rectangle2D bounds = shape.getBounds();

			if(shape.contains(x, y)) {
				if(wxy != null) {
					wxy[0] = x - (int)bounds.getMinX();
					wxy[1] = y - (int)bounds.getMinY();
				}
				return w;
			}

			int c = bounds.outcode(x, y);
			if(c == Rectangle2D.OUT_LEFT || c == Rectangle2D.OUT_TOP) {
				b = m;
			} else {
				a = m + 1;
			}
		}
		return null;
	}

	public UiWidget addDynamic(UiWidget widget) {
		if(container != null && dynamicSet.isEmpty()) {
			container.addDynamic(this);
		}
		dynamicSet.add(widget);
		return widget;
	}

	public boolean removeDynamic(UiWidget widget) {
		boolean removed = dynamicSet.remove(widget);
		if(removed && container != null && dynamicSet.isEmpty()) {
			container.removeDynamic(this);
		}
		return removed;
	}

	public class State extends NullState {
		UiWidget mouseOver = null;
		
		@Override
		public Event.Status onKeyDown(Event.Status es, InputEvent.Key key) {
			if(activeChild != null) {
				return activeChild.getState().onKeyDown(es, key);
			}
			return super.onKeyDown(es, key);
		}

		@Override
		public Event.Status onKeyUp(Event.Status es, InputEvent.Key key) {
			if(activeChild != null) {
				return activeChild.getState().onKeyUp(es, key);
			}
			return super.onKeyUp(es, key);
		}

		@Override
		public Event.Status onMouseDown(Event.Status es, int x, int y, int button) {
			int [] wxy = new int[2];
			UiWidget w = getChildAt(x, y, wxy);
			if(w != null) {
				return w.getState().onMouseDown(es, wxy[0], wxy[1], button);
			}
			return super.onMouseDown(es, x, y, button);
		}

		@Override
		public Event.Status onMouseUp(Event.Status es, int x, int y, int button) {
			int [] wxy = new int[2];
			UiWidget w = getChildAt(x, y, wxy);
			if(w != null) {
				return w.getState().onMouseUp(es, wxy[0], wxy[1], button);
			}
			return super.onMouseDown(es, x, y, button);
		}

		@Override
		public Event.Status onMouseOver(Event.Status es, int x, int y) {
			int [] wxy = new int[2];
			UiWidget w = getChildAt(x, y, wxy);
			if(w != mouseOver) {
				if(mouseOver != null) {
					mouseOver.getState().onMouseOut(Event.Status.PROPAGATING);
				}
				mouseOver = w;
				if(mouseOver != null) {
					return w.getState().onMouseOver(es, wxy[0], wxy[1]);
				}
			}
			return super.onMouseOver(es, x, y);
		}

		@Override
		public Event.Status onMouseOut(Event.Status es) {
			if(mouseOver != null) {
				es = mouseOver.getState().onMouseOut(es);
				mouseOver = null;
				return es;
			}
			return super.onMouseOut(es);
		}

		@Override
		public Event.Status onScroll(Event.Status es, int x, int y, float amtx, float amty, float amtz) {
			int [] wxy = new int[2];
			UiWidget w = getChildAt(x, y, wxy);
			if(w != null) {
				return w.getState().onScroll(es, x, y, amtx, amty, amtz);
			}
			return super.onScroll(es, x, y, amtx, amty, amtz);
		}
	}
}
